package com.franklin.ideaplugin.easytesting.spring.web.controller;

import com.franklin.ideaplugin.easytesting.common.entity.ETRsp;
import com.franklin.ideaplugin.easytesting.common.entity.MethodInvokeData;
import com.franklin.ideaplugin.easytesting.common.log.ILogger;
import com.franklin.ideaplugin.easytesting.common.log.LoggerFactory;
import com.franklin.ideaplugin.easytesting.common.utils.JsonUtils;
import com.franklin.ideaplugin.easytesting.common.utils.MethodUtils;
import com.franklin.ideaplugin.easytesting.core.invoke.interceptor.MethodInvokeInterceptorFactory;
import com.franklin.ideaplugin.easytesting.core.rpc.NettyServer;
import com.franklin.ideaplugin.easytesting.spring.constants.StrPool;
import com.franklin.ideaplugin.easytesting.spring.invoke.SpringMethodInvoker;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;

/**
 * @author Ye Junhui
 * @since 2023/5/18
 */
@RestController
@ConditionalOnBean(SpringMethodInvoker.class)
@RequiredArgsConstructor
public class EasyTestingWebMethodController {

    private static final ILogger log = LoggerFactory.getLogger(EasyTestingWebMethodController.class);

    private final SpringMethodInvoker springMethodInvoker;

    @PostMapping(StrPool.INNER_REQUEST_PATH)
    public ETRsp<Object> execute(@RequestBody MethodInvokeData methodInvokeData){
        try {
            Class<?> targetClass = Class.forName(methodInvokeData.getClassQualifiedName());
            Class<?>[] paramTypes = MethodUtils.resolveParamTypes(methodInvokeData);
            Method declaredMethod = targetClass.getDeclaredMethod(methodInvokeData.getMethodName(), paramTypes);
            declaredMethod.setAccessible(true);

            //参数值
            Object[] params = methodInvokeData.getParameterMap().values().stream()
                    .map(MethodUtils::parseValue)
                    .toArray();
            //generic type
            Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();

            //拦截器
            MethodInvokeInterceptorFactory.getInstance().intercept(methodInvokeData,genericParameterTypes,params);

            //重置请求头，避免重新请求
            methodInvokeData.setHeaderMap(new LinkedHashMap<>());
            Object result = springMethodInvoker.invoke(methodInvokeData, targetClass, declaredMethod,paramTypes , params);
            if (result instanceof Throwable){
                Throwable throwable = (Throwable) result;
                return ETRsp.fail(throwable);
            }
            log.info("Easy-Testing -> method execute result : \n{}", JsonUtils.toJSONString(result));
            return ETRsp.success(result);
        }catch (Throwable e){
            log.error("Easy-Testing -> method execute fail",e);
            return ETRsp.fail(e);
        }
    }
}
